package com.yonyougov.devops.cruxcisupport.k8s.deployment;

import com.yonyougov.devops.cruxcisupport.enums.EnvInfoTypeEnum;
import com.yonyougov.devops.cruxcisupport.k8s.Constant;
import com.yonyougov.devops.cruxcisupport.k8s.K8sRepository;
import com.yonyougov.devops.cruxcisupport.k8s.error.K8sConfIgNotFindException;
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
import io.fabric8.kubernetes.api.model.apps.RollingUpdateDeployment;
import io.fabric8.kubernetes.client.KubernetesClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

@Slf4j
@Repository
public class DeploymentRepositoryImpl implements DeploymentRepository{

    @Value("${k8s.livenessProbe.initialDelaySeconds:600}")
    private Integer livenessProbeInitialDelaySeconds;

    @Value("${k8s.livenessProbe.periodSeconds:60}")
    private Integer livenessProbePeriodSeconds;

    @Value("${needPrometheus.apps:crux-logger}")
    private String needPrometheusApps;

    private final K8sRepository k8sRepository;

    public DeploymentRepositoryImpl(K8sRepository k8sRepository) {
        this.k8sRepository = k8sRepository;
    }

    @Override
    public Deployment createOrReplace(DeploymentRequest deploymentRequest) throws K8sConfIgNotFindException {
        return rollingUpgrade(deploymentRequest);
    }


    @Override
    public Deployment rollingUpgrade(DeploymentRequest deploymentRequest) throws K8sConfIgNotFindException {
        KubernetesClient client = k8sRepository.createDefaultClient(deploymentRequest.getNameSpace());
        Deployment result = null;
        try{

            // 用于触发镜像拉取
            Long oldTerminationGracePeriodSeconds = null;
            try {
                oldTerminationGracePeriodSeconds = client.apps().deployments().inNamespace(deploymentRequest.getNameSpace()).withName(deploymentRequest.getContainerName()).get().getSpec().getTemplate().getSpec().getTerminationGracePeriodSeconds();
            } catch (Exception e) {
                oldTerminationGracePeriodSeconds = 30L;
            }
            Long terminationGracePeriodSeconds = 30L == oldTerminationGracePeriodSeconds ? 31L : 30L;
            // 构建Deployment
            Deployment deployment = new DeploymentBuilder()
            //Deployment deployment = client.apps().deployments().inNamespace(deploymentRequest.getNameSpace()).withName(deploymentRequest.getContainerName()).edit()
                    .withNewMetadata()
                    .withName(deploymentRequest.getMetadataName())
                    .endMetadata()
                    .withNewSpec()
                    .withReplicas(deploymentRequest.getReplicas())
                    //.withMinReadySeconds(120)
                    //滚动策略
                    .withNewStrategy()
                    .withType("RollingUpdate")
                    .withRollingUpdate(new RollingUpdateDeployment(new IntOrString("25%"), new IntOrString("0%")))
                    .endStrategy()
                    .withNewTemplate()
                    .withNewMetadata()
                    .addToLabels(deploymentRequest.getLabels()).endMetadata()
                    .withNewSpec()
                    // 用于触发镜像拉取
                    .withTerminationGracePeriodSeconds(terminationGracePeriodSeconds)
                    .addNewImagePullSecret()
                    .withName(Constant.SECRET_NAME).endImagePullSecret()
                    .addNewHostAlias()
                    .withIp("123.103.9.104")
                    .withHostnames("dev.yonyougov.top").endHostAlias()
                    .addNewContainer()
                    .withName(deploymentRequest.getContainerName())
                    //// 存活检查
                    //.withLivenessProbe(livenessProbe)
                    //// 就绪检查
                    //.withReadinessProbe(readinessProbe)
                    .withImage(deploymentRequest.getImageName())
                    .withImagePullPolicy("Always")
                    .withEnv()
                    .addNewEnvFrom()
                    .withConfigMapRef(new ConfigMapEnvSource("fap-env", null)).endEnvFrom()
                    .addNewVolumeMount()
                    .withName("host-time")
                    .withMountPath("/etc/localtime").endVolumeMount()
//                    .withNewLivenessProbe()
//                    .withHttpGet(httpGetAction)
//                    .withInitialDelaySeconds(600)
//                    .withPeriodSeconds(60).endLivenessProbe()
                    .endContainer()
                    .addNewVolume()
                    .withName("host-time")
                    .withNewHostPath()
                    .withPath("/etc/localtime")
                    .endHostPath().endVolume()
                    .endSpec()
                    .endTemplate()
                    .withNewSelector()
                    .addToMatchLabels(deploymentRequest.getMatchLabels())
                    .endSelector()
                    .endSpec()
                    //.done();
                    .build();

            // 处理环境变量和配置映射
            if(CollectionUtils.isNotEmpty(deploymentRequest.getEnvContext())){
                List<EnvInfo> envInfos = deploymentRequest.getEnvContext();
                List<EnvVar> defaultStringKeyValue = new ArrayList<>();
                List<EnvVar> metadataRefKeyValue = new ArrayList<>();
                ArrayList<Volume> volumes = new ArrayList<>();
                ArrayList<VolumeMount> volumeMounts = new ArrayList<>();
                for (EnvInfo keyValue : envInfos) {
                    log.info("eeeeee测试验证:{}",keyValue);
                    if (keyValue.getEnvInfoType() == EnvInfoTypeEnum.metadateRef.getCode()) {
                        EnvVar envVar = new EnvVar();
                        envVar.setName(keyValue.getName());
                        EnvVarSource envVarSource = new EnvVarSource();
                        ObjectFieldSelector objectFieldSelector = new ObjectFieldSelector();
                        objectFieldSelector.setFieldPath(keyValue.getValue());
                        envVarSource.setFieldRef(objectFieldSelector);
                        envVar.setValueFrom(envVarSource);
                        defaultStringKeyValue.add(envVar);
                    }else if(keyValue.getEnvInfoType() == EnvInfoTypeEnum.fileMap.getCode()){
                        String name = UUID.randomUUID().toString();
                        // 卷映射
                        Volume volume = new Volume();
                        ArrayList<KeyToPath> keyToPaths = new ArrayList<>();
                        KeyToPath keyToPath = new KeyToPath();
                        keyToPath.setKey(keyValue.getName());
                        keyToPath.setPath(keyValue.getName());
                        keyToPath.setMode(511);
                        keyToPaths.add(keyToPath);
                        volume.setConfigMap(new ConfigMapVolumeSource(511,keyToPaths,"fap-env", true));
                        volume.setName(name);
                        volumes.add(volume);

                        // 挂载
                        VolumeMount volumeMount = new VolumeMount();
                        volumeMount.setMountPath(keyValue.getAbsolutePath());
                        volumeMount.setSubPath(keyValue.getName());
                        volumeMount.setName(name);
                        volumeMounts.add(volumeMount);
                    } else if(keyValue.getEnvInfoType() == EnvInfoTypeEnum.defaultString.getCode()){
                        EnvVar envVar = new EnvVar();
                        envVar.setName(keyValue.getName());
                        envVar.setValue(keyValue.getValue());
                        metadataRefKeyValue.add(envVar);
                    }
                }
                deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv().addAll(defaultStringKeyValue);
                deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getEnv().addAll(metadataRefKeyValue);
                deployment.getSpec().getTemplate().getSpec().getVolumes().addAll(volumes);
                deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getVolumeMounts().addAll(volumeMounts);
            }

            // 生命周期钩子
            if(ObjectUtils.isNotEmpty(deploymentRequest.getLifecycleContext())){
                LifecycleContext lifecycleContext = deploymentRequest.getLifecycleContext();
                Lifecycle lifecycle = new Lifecycle();

                ExecAction execAction = new ExecAction();
                ExecAction execAction2 = new ExecAction();

                execAction.setCommand(Arrays.asList("/bin/sh","-c",lifecycleContext.getStartPostScript()));
                execAction2.setCommand(Arrays.asList("/bin/sh","-c",lifecycleContext.getEndPreScript()));

                Handler handler = new Handler();
                Handler handler2 = new Handler();
                handler.setExec(execAction);
                handler2.setExec(execAction2);

                if(ObjectUtils.isNotEmpty(lifecycleContext.getStartPostScript())){
                    lifecycle.setPostStart(handler);
                }
                if(ObjectUtils.isNotEmpty(lifecycleContext.getEndPreScript())){
                    lifecycle.setPreStop(handler2);
                }
                deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setLifecycle(lifecycle);
                log.info("{}服务生命周期开始");
            }


            // 健康检查（存活检查，就绪检查）
            if(null != deploymentRequest.getHealthCheckContext()){
                log.info("健康检查开启");
                List<HTTPHeader> httpHeaders = deploymentRequest.getHealthCheckContext().getHttpHeaders();
                httpHeaders.add(new HTTPHeader("Health-Check","OK"));
                // 存活检查
                Probe livenessProbe = new Probe();
                HTTPGetAction livenessProbeHttpGetAction = new HTTPGetAction();
                livenessProbeHttpGetAction.setPath(deploymentRequest.getHealthCheckContext().getPath());
                livenessProbeHttpGetAction.setPort(new IntOrString(deploymentRequest.getHealthCheckContext().getPort()));
                livenessProbeHttpGetAction.setHttpHeaders(httpHeaders);
                livenessProbe.setHttpGet(livenessProbeHttpGetAction);
                livenessProbe.setInitialDelaySeconds(livenessProbeInitialDelaySeconds);
                livenessProbe.setPeriodSeconds(livenessProbePeriodSeconds);
                deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setLivenessProbe(livenessProbe);
                // 就绪检查
                Probe readinessProbe = new Probe();
                HTTPGetAction readinessProbeHttpGetAction = new HTTPGetAction();
                readinessProbeHttpGetAction.setPath(deploymentRequest.getHealthCheckContext().getPath());
                readinessProbeHttpGetAction.setPort(new IntOrString(deploymentRequest.getHealthCheckContext().getPort()));
                readinessProbeHttpGetAction.setHttpHeaders(httpHeaders);
                readinessProbe.setHttpGet(livenessProbeHttpGetAction);
                deployment.getSpec().getTemplate().getSpec().getContainers().get(0).setReadinessProbe(readinessProbe);
            }
            client.apps().deployments().inNamespace(deploymentRequest.getNameSpace()).createOrReplace(deployment);
            result = deployment;
        }finally {
            client.close();
        }
        return result;
    }

    @Override
    public Deployment get(String nameSpace, String appName) throws K8sConfIgNotFindException {
        KubernetesClient client = k8sRepository.createDefaultClient(nameSpace);
        return get(client,nameSpace,appName);
    }

    @Override
    public Deployment get(KubernetesClient client, String nameSpace, String appName) {
        return client.apps().deployments().inNamespace(nameSpace).withName(appName).get();
    }

    @Override
    public void delete(String nameSpace, String appName) throws K8sConfIgNotFindException {
        KubernetesClient client = k8sRepository.createDefaultClient(nameSpace);
        delete(client,nameSpace,appName);
    }

    @Override
    public void delete(KubernetesClient client, String nameSpace, String appName) {
        Deployment deployment = get(client, nameSpace, appName);
        if(null == deployment){
            log.debug("Deployment[]: {} don't exist",appName);
        }else{
            log.debug("Deployment[]: {} 已经存在",appName);
            client.apps().deployments().inNamespace(nameSpace).withName(appName).delete();
        }
    }
}
